from collections.abc import Iterator

# https://pqyiz16he9g.feishu.cn/docx/KRAOdF0ZeouOGzxeRn0cUF5onrf

# 3、可迭代对象
# 定义：如果一个类中包含__iter__函数，并且返回一个迭代器，则称这个类创建的对象为可迭代对象。所以：迭代器对象也是可迭代对象。

# 什么是迭代器和可迭代对象，他们之间有什么关系？
# 1.迭代器： 如果一个对象同时实现了__iter__函数和__next__函数，它就是迭代器
# 2.可迭代对象： 如果一个对象实现了__iter__函数，那么这个对象就是可迭代对象
# 3.他们之间有什么关系： 迭代器一定是可迭代对象，反之则不成立，可迭代对象的__iter__函数必须返回一个迭代器
#
# 迭代器优点：
# 1. 提供了一种通用不依赖索引的迭代取值方式；
# 2. 节省内存，迭代器在内存中相当于只占一个数据的空间：因为每次取值都上一条数据会在内存释放，加载当前的此条数据。

# 迭代器缺点：
# 1. 因为有next函数，即只能往后取值,不能往前，取值不如按照索引的方式灵活，不能取指定的某一个值；
# 2. 无法预测迭代器的长度。


# 模仿一个range函数的迭代器对象
class MyRange(object):
    def __init__(self,stop_num):
        self.stop_num = stop_num
        self.num = -1 # 数字是从0开始的

    # 返回自己
    def __iter__(self):
        print('__iter__')
        return self
    # 返回下一条数据
    def __next__(self):
        print('__next__')
        self.num += 1
        if self.num == self.stop_num: # 如果返回到最后一条数据，说明没有下一条数据了，抛出异常
            raise StopIteration
        return self.num

class ItRange(object):
    def __iter__(self):
        return MyRange(3)



# 定义一个可迭代对象


if __name__ == '__main__':
    my_range = MyRange(5)
    # print(my_range.__next__())
    # print(my_range.__next__())
    # print(my_range.__next__())
    # print(my_range.__next__())
    # print(my_range.__next__())

    print(isinstance(my_range, Iterator)) # True 是一个迭代器对象
    for num in my_range:
        print(num)

    print('*'*50)
    obj1 = MyRange(6)  # 迭代器对象
    obj2 = range(6)  # 可迭代对象
    obj3 = obj2.__iter__()  # 返回迭代器对象

    obj4 = ItRange()
    obj5 = obj4.__iter__()

    print(isinstance(obj4, Iterator)) # False obj4 不是一个可迭代对象
    print(isinstance(obj5, Iterator)) # True obj5  是一个可迭代对象
    # 为什么obj4是一个可迭代对象，obj5 是一个可迭代对象？ 看定义。 用dir打印这两个对象的结构
    print(dir(obj4)) # obj4 对象结构有 __iter__ 但是没有 __next__ 函数，所以 obj4 是一个迭代对象
    #输出：['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
    print(dir(obj5)) # obj5 对象结构有 __iter__ 和 __next__ 这两个函数，所以obj5 是一个可迭代对象
    #输出：['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'num', 'stop_num']

    print(dir([1, 2, 3])) # 列表是一个 迭代对象。 有 __next__

    # 所有的可迭代对象和迭代器对象，都可以通过for循环遍历。
